package com.suray.wcs.service.scheduling;

import com.suray.basic.wcs.path.core.Node;
import com.suray.basic.wcs.path.core.Route;
import com.suray.basic.wcs.path.core.api.impl.DifFloorRouteFactoryImp;
import com.suray.basic.wcs.path.enums.NodeStatus;
import com.suray.basic.wcs.path.enums.NodeType;
import com.suray.basic.wcs.plc.utils.PlcExecuteException;
import com.suray.basic.wcs.utils.Action;
import com.suray.basic.wcs.utils.Coord;
import com.suray.basic.wcs.utils.LockNode;
import com.suray.commin.core.utils.SpringContextHolder;
import com.suray.type.ErrorLevel;
import com.suray.wcs.res.enums.PalletStatus;
import com.suray.wcs.res.enums.RgvStatus;
import com.suray.wcs.service.core.path.api.DeadlockHandle;
import com.suray.wcs.service.core.path.api.impl.DeadLockHandleImpl;
import com.suray.wcs.service.core.util.LockNodeManager;
import com.suray.wcs.service.enums.*;
import com.suray.wcs.service.init.SystemInit;
import com.suray.wcs.service.pojo.Rgv;
import com.suray.wcs.service.rescontact.ActionHandle;
import com.suray.wcs.service.system.bo.NotificationBO;
import com.suray.wcs.service.system.entity.NodeDB;
import com.suray.wcs.service.system.entity.TaskDB;
import com.suray.wcs.service.system.entity.WareHouseDB;
import com.suray.wcs.service.system.service.impl.*;
import com.suray.wcs.service.thread.BaseRgvThread;
import org.apache.commons.collections.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class RgvTaskTimer extends BaseRgvThread {
    private final TaskDBServiceImpl taskDBService = SpringContextHolder.getBean(TaskDBServiceImpl.class);
    private final NodeDBServiceImpl nodeDBService = SpringContextHolder.getBean(NodeDBServiceImpl.class);
    private NotificationServiceImpl notificationService = SpringContextHolder.getBean(NotificationServiceImpl.class);
    private int freeCount = 0;
    private boolean notificationFlag = false;
    private Logger log = LoggerFactory.getLogger(this.getClass());
    private DeadlockHandle deadlockHandle = SpringContextHolder.getBean(DeadLockHandleImpl.class);

    public RgvTaskTimer(Rgv rgv) {
        super(rgv.getRgvName() + "自动执行任务", rgv);
    }

    public void start() {
        ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
        executorService.scheduleWithFixedDelay(() -> {
            Thread.currentThread().setName(parentRgv.getRgvName() + "定时任务执行");
            try {
                //自动结束充电设置
                if (
                        parentRgv.getElectricStatus() == ElectricStatus.FULL_BATTERY
                        && parentRgv.getRgvStatus() == RgvStatus.CHARGING
                        && (parentRgv.getRgvDB().getBalanceBat() == null || !parentRgv.getRgvDB().getBalanceBat()
                                || parentRgv.getRgvError().getBatteryError().getValue() <= ErrorLevel.ERROR_LEVEL_3.getValue())
                ) {
                    try {
                        ActionHandle.actionHandle(null, null, parentRgv, null, null, Action.ENDCHARGE);
                        TaskDB task = new TaskDB();
                        NodeDB nodedb = nodeDBService.getNodeDB(parentRgv.getCurLocation());
                        Node backNode = getBackNode(nodedb);
                        task.setEndNode(backNode);
                        task.setTaskType(TaskType.MOVE_UNLOADED);
                        task.setProcessingRgvNo(parentRgv.getRgvDB().getRgvNo());
                        task.setRgvProgress(TaskRgvProgress.ASSIGNED_TO_RGV);
                        taskDBService.insertTask(Collections.singletonList(task));
                    } catch (PlcExecuteException | IOException e) {
                        log.error("关闭充电桩异常" + e.getMessage());
                    } catch (RuntimeException e) {
                        log.error("充电桩可行驶方向不正确，请手动添加后退方案" + e.getMessage());
                    }
                    log.info(parentRgv + "自动结束充电");
                    notificationFlag = false;
                }
                //空闲五分钟并且电量处于正常电量模式,每30秒检测一次
                if ((parentRgv.getRgvDB().getFreeTime() != null && freeCount >= parentRgv.getRgvDB().getFreeTime() * 6
                        && freeCount != 0)) {
                    if (SystemInit.checkIsCanCharge(parentRgv)) {
                        if (!parentRgv.isHasCargo()) {
                            if (parentRgv.getElectricStatus() == ElectricStatus.NOR_BATTERY
                                    || parentRgv.getRgvError().getBatteryError().getValue()
                                    <= ErrorLevel.ERROR_LEVEL_2.getValue()) {
                                if (parentRgv.getCurTask() == null || parentRgv.getCurTask().getTaskId() == null) {
                                    List<TaskDB> taskDBs = taskDBService.findUnFinishTaskByDistrictAndRgvNOAndTaskType(
                                            parentRgv.getRgvDB().getRgvNo(), TaskType.CHARGE);
                                    if ((taskDBs == null || taskDBs.size() < 1) && parentRgv.getRgvStatus() != RgvStatus.ERROR) {
                                        Node bestNode = getBastAvailableChargeLocation(0, 0);
                                        if (bestNode != null) {
                                            createChargeTask(bestNode);
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
                //自动充电设置
                if (SystemInit.checkIsCanCharge(parentRgv) && freeCount % 10 == 0) {
                    if (
                            (parentRgv.getElectricStatus() == ElectricStatus.LOW_BATTERY
                                && parentRgv.getRgvStatus() != RgvStatus.CHARGING)
                                    || parentRgv.getRgvError().getBatteryError().getValue()
                                    <= ErrorLevel.ERROR_LEVEL_2.getValue()
                    ) {
                        List<TaskDB> tasks = taskDBService.findUnFinishTaskByDistrictAndRgvNOAndTaskType(parentRgv.getRgvDB().getRgvNo().intValue(), TaskType.CHARGE);
                        if (tasks == null || tasks.size() < 1) {
                            if (!parentRgv.isHasCargo()) {
                                if (parentRgv.getRgvStatus() != RgvStatus.ERROR && parentRgv.getRgvStatus() != RgvStatus.CHARGING) {
                                    Node bestNode = getBastAvailableChargeLocation(SystemInit.RGVS.size(), 0);
                                    if (bestNode != null) {
                                        createChargeTask(bestNode);
                                    } else if (!notificationFlag) {
                                        NotificationBO notificationBO = new NotificationBO();
                                        notificationBO.setContent(parentRgv.getRgvName() + "找不到可用的充电桩。");
                                        notificationBO.setNotificationType(NotificationTypeEnum.WARN.getType());
                                        notificationBO.setIsRead(false);
                                        notificationBO.setSeverityLevel(1);
                                        notificationService.insertNotification(notificationBO);
                                        notificationFlag = true;
                                    }
                                }
                            }
                        }
                    }
                }
                //空闲五分钟并且无任务自动回缓存位
                if (parentRgv.getRgvDB().getFreeTime() != null && freeCount >= parentRgv.getRgvDB().getFreeTime() * 6 && freeCount != 0) {
                    if (SystemInit.checkIsGoFreeLocation(parentRgv) && parentRgv.getPalletStatus() == PalletStatus.DOWN) {
                        if (parentRgv.getCurTask() == null || parentRgv.getCurTask().getTaskId() == null) {
                            if (CollectionUtils.isEmpty(taskDBService.findPreProcessingTasksByRgvNo(parentRgv.getRgvDB().getRgvNo()))) {
                                if (org.apache.commons.lang3.StringUtils.isNotEmpty(parentRgv.getRgvDB().getFreeLocation())) {
                                    NodeDB freeLocationNodeDB = nodeDBService.getNodeDB(Node.parseGrid(parentRgv.getRgvDB().getFreeLocation()));
                                    if (freeLocationNodeDB != null && !parentRgv.getCurLocation().equals(Node.parseGrid(parentRgv.getRgvDB().getFreeLocation()))) {
                                        buildTask(Node.parseGrid(parentRgv.getRgvDB().getFreeLocation()), parentRgv.getRgvDB().getRgvNo());
                                        freeCount = 0;
                                    }
                                }
                            }
                        }
                    }
                }
                //空闲10S并且无任务自动回缓存位
                if (SystemInit.checkIsGoFreeLocation(parentRgv) && parentRgv.getPalletStatus() == PalletStatus.DOWN && freeCount % 5 == 0) {
                    NodeDB nodeDB = nodeDBService.getNodeDB(parentRgv.getCurLocation());
                    if (taskDBService.getUnFinishTaskByRgvNo(parentRgv.getRgvDB().getRgvNo()).size() == 0) {
                        if (nodeDB != null && !RoadType.D.getVal().equals(nodeDB.getRoadType()) && CollectionUtils.isEmpty(taskDBService.findPreProcessingTasksByRgvNo(parentRgv.getRgvDB().getRgvNo()))) {
                            List<Coord> availableNodeList;
                            availableNodeList = nodeDBService.findSpecifiedNodesByRoadType(NodeType.P, null, parentRgv.getCurLocation().getZ(), RoadType.D.getVal());
                            availableNodeList.removeAll(LockNodeManager.getLockedNodes());
                            Node bestNode = deadlockHandle.getBestAvoidNode(availableNodeList, parentRgv);
                            if (bestNode != null) {
                                buildTask(bestNode, parentRgv.getRgvDB().getRgvNo());
                                freeCount = 0;
                            }
                        }
                    }
                }
                if (parentRgv.getCurTask() == null || parentRgv.getCurTask().getTaskId() == null) {
                    freeCount++;
                } else {
                    freeCount = 0;
                }
            } catch (Exception e) {
                e.printStackTrace();
                log.error(parentRgv.getRgvName() + "充电线程出现异常");
            }
        }, 10, 10, TimeUnit.SECONDS);
        log.info(super.parentRgv.getRgvDB().getRgvNo() + "号车自动充电线程启动");
    }

    /**
     * 从创建充电任务
     * @param bestNode
     */
    private void createChargeTask(Node bestNode) {
        TaskDB task = new TaskDB();
        task.setEndNode(bestNode);
        task.setTaskType(TaskType.CHARGE);
        task.setProcessingRgvNo(parentRgv.getRgvDB().getRgvNo());
        task.setRgvProgress(TaskRgvProgress.ASSIGNED_TO_RGV);
        taskDBService.insertTask(Collections.singletonList(task));
        log.info(parentRgv + "自动充电");
        freeCount = 0;
    }

    /**
     * 创建空载行驶任务
     *
     * @param bestNode
     * @param rgvNo
     */
    private void buildTask(Node bestNode, Integer rgvNo) {
        TaskDB task = new TaskDB();
        task.setEndNode(bestNode);
        task.setTaskType(TaskType.MOVE_UNLOADED);
        task.setProcessingRgvNo(rgvNo);
        task.setRgvProgress(TaskRgvProgress.ASSIGNED_TO_RGV);
        taskDBService.insertTask(Collections.singletonList(task));
        log.info("{}状态：{},当前没有要执行的任务生成去节点：{}的空载任务：{}", parentRgv.getRgvName(), parentRgv.getRgvStatus(), bestNode, task.getTaskId());
    }

    /**
     * 获取到最优的充电位置
     *
     * @param tryCount     尝试查找次数
     * @param currentCount 当前已经查找次数
     * @return
     */
    public Node getBastAvailableChargeLocation(int tryCount, int currentCount) {
        Node bestNode = null;
        if (!StringUtils.isEmpty(parentRgv.getRgvDB().getChargeLocation())) {
            bestNode = Node.parseGrid(parentRgv.getRgvDB().getChargeLocation());
        }

        if (bestNode == null) {
            bestNode = getBastAvailableChargeLocation(getAvailableChargeLocations(parentRgv));
        }

        if (bestNode == null) {
            List<LockNode> lockNodes = new ArrayList<>(LockNodeManager.getLockedNodes());
            List<Node> chargeNodes = getAllChargeLocations();
            lockNodes.retainAll(chargeNodes);
            List<LockNode> availLockNodes = new ArrayList<>();
            for (LockNode lockNode : lockNodes) {
                if (lockNode.getLockedBy() != null && lockNode.getLockedBy() instanceof Rgv) {
                    Rgv rgv = (Rgv) lockNode.getLockedBy();
                    rgv = SystemInit.getByRgvNo(rgv.getRgvDB().getRgvNo());
                    if (rgv.getElectricStatus() != ElectricStatus.LOW_BATTERY &&
                            rgv.getElectricStatus() != ElectricStatus.ERROR_BATTERY) {
                        availLockNodes.add(lockNode);
                    }
                }
            }
            chargeNodes.retainAll(availLockNodes);
            bestNode = getBastAvailableChargeLocation(chargeNodes);
        }

        if (bestNode != null) {
            boolean isLock = false;
            boolean moveOtherRgv = false;
            int second = 0;
            while (!isLock) {
                try {
                    second++;
                    LockNodeManager.addLockedNodes(Collections.singleton(bestNode), parentRgv);
                    isLock = true;
                } catch (Exception e) {
                    Object obj = LockNodeManager.checkNodeListLockBy(Collections.singletonList(bestNode), parentRgv);
                    if (obj != null && obj instanceof Rgv && !moveOtherRgv) {
                        Rgv rgv = (Rgv) obj;
                        TaskDB taskDB = taskDBService.findFirstCanExecuteTask(rgv.getRgvDB().getRgvNo());
                        if (rgv.getElectricStatus() != ElectricStatus.LOW_BATTERY
                                && rgv.getElectricStatus() != ElectricStatus.ERROR_BATTERY
                                && taskDB == null
                                && rgv.getCurLocation() != null
                                && rgv.getBattery() > rgv.getRgvDB().getAssignBatterySize()) {
                            try {
                                ActionHandle.actionHandle(null, null, rgv, null, null, Action.ENDCHARGE);
                                List<Coord> availableNodeList;
                                Boolean moreOrOne = SystemInit.wareHouseDB.getMoreForOne();
                                if (moreOrOne) {
                                    availableNodeList = nodeDBService.findSpecifiedNodes(parentRgv.isHasCargo() ? NodeType.R : NodeType.P, parentRgv.isHasCargo() ? NodeStatus.N
                                            : null, rgv.getCurLocation().getZ());
                                } else {
                                    availableNodeList = nodeDBService.findSpecifiedNodes(parentRgv.isHasCargo() ? NodeType.R : NodeType.P, parentRgv.isHasCargo() ? NodeStatus.N
                                            : null, parentRgv.getCurLocation().getZ());
                                }
                                availableNodeList.removeAll(LockNodeManager.getLockedNodes());
                                SystemInit.removeLockNode(availableNodeList);
                                DifFloorRouteFactoryImp routeFactoryImp =
                                        new DifFloorRouteFactoryImp(nodeDBService.getNodeList(),
                                                LockNodeManager.getLockedNodesExceptSelf(rgv),
                                                rgv.generateWCSTimeModuleBO(), rgv.getRgvDB().getRgvType());
                                Node avoidNode = routeFactoryImp.getLatelyNode(rgv.getCurLocation(), availableNodeList,
                                        !rgv.isHasCargo() ? false : true);
                                if (avoidNode != null) {
                                    TaskDB task = new TaskDB();
                                    task.setEndNode(avoidNode);
                                    task.setTaskType(TaskType.MOVE_UNLOADED);
                                    task.setProcessingRgvNo(rgv.getRgvDB().getRgvNo());
                                    taskDBService.insertTask(Collections.singletonList(task));
                                    log.info(parentRgv + "去充电， 使" + rgv + "移动到" + avoidNode);
                                    moveOtherRgv = true;
                                }
                            } catch (PlcExecuteException plcExecuteException) {
                                log.error("关闭充电桩异常" + plcExecuteException.getMessage());
                            } catch (IOException i) {
                                log.error("关闭充电桩异常" + i.getMessage());
                            }
                            log.info(rgv + "自动结束充电");
                        }
                    }

                    try {
                        TimeUnit.SECONDS.sleep(1);
                    } catch (InterruptedException exc) {
                    }
                    if (second > 10) {
                        bestNode = null;
                    }
                }
                if (second > 10) {
                    bestNode = null;
                    isLock = true;
                }
            }
        }

        if (bestNode == null) {
            currentCount++;
            if (currentCount < tryCount) {
                log.info(parentRgv + "未找到充电桩，重试：" + currentCount);
                return getBastAvailableChargeLocation(tryCount, currentCount);
            }
        }
        return bestNode;
    }

    /**
     * 从充电桩集合中获取最优位置
     *
     * @param nodes 充电桩集合
     * @return
     */
    private Node getBastAvailableChargeLocation(List<Node> nodes) {
        List<Integer> layers = new ArrayList<>();
        layers.add(parentRgv.getCurLocation().getZ());
        DifFloorRouteFactoryImp routeFactory = new DifFloorRouteFactoryImp(nodeDBService.getNodeByLayer(layers),
                null, parentRgv.generateWCSTimeModuleBO(), 0);
        int minCost = -1;
        Node bestNode = null;
        Boolean moreOrOne = SystemInit.wareHouseDB.getMoreForOne();
        for (Node node : nodes) {
            List<Route> toUnLoad;
            if (parentRgv.getCurLocation() != null && moreOrOne != null && !moreOrOne.booleanValue()) {
                if (node.getZ() != parentRgv.getCurLocation().getZ()) {
                    boolean flag = false;
                    List<Rgv> rgvS = SystemInit.getRgvList();
                    for (Rgv rgv : rgvS) {
                        if (rgv.getRgvDB().getRgvNo().intValue() == parentRgv.getRgvDB().getRgvNo().intValue()) {
                            continue;
                        }
                        if (rgv.getRgvDB().getLocationNode().getZ() == node.getZ()) {
                            flag = true;
                        }
                    }
                    if (flag) {
                        continue;
                    }
                    List<Integer> rgvNos = taskDBService.getRgvNoByCoord(node);
                    if (rgvNos.size() > 0) {
                        continue;
                    }
                }
            }
            try {
                toUnLoad = routeFactory.createRoute(parentRgv.getCurLocation(), node, false);
            } catch (Exception e) {
                continue;
            }
            int currentCost = 0;
            for (Route route : toUnLoad) {
                currentCost += route.getCost();
            }
            if (minCost == -1) {
                bestNode = node;
                minCost = currentCost;
            }
            if (minCost > currentCost) {
                bestNode = node;
                minCost = currentCost;
            }
        }
        return bestNode;
    }

    /**
     * 获取类型是充电桩的节点
     * @return
     */
    private List<Node> getAllChargeLocations() {
        List<Node> nodes = nodeDBService.getNodeByType(NodeType.C);
        return nodes;
    }

    /**
     * 获取节点未被锁定的充电桩位置
     * @param rgv
     * @return
     */
    private List<Node> getAvailableChargeLocations(Rgv rgv) {
        List<Node> nodes = nodeDBService.getNodeByType(NodeType.C);

        List<LockNode> lockNodes = new ArrayList<>(LockNodeManager.getLockedNodes());
        List<LockNode> availLockNodes = new ArrayList<>();
        for (LockNode lockNode : lockNodes) {
            if (lockNode.getLockedBy() != null && lockNode.getLockedBy() instanceof Rgv) {
                Rgv rgvLock = (Rgv) lockNode.getLockedBy();
                if (rgv.getRgvDB().getRgvNo().intValue() == rgvLock.getRgvDB().getRgvNo().intValue()) {
                    availLockNodes.add(lockNode);
                }
            }
        }
        lockNodes.removeAll(availLockNodes);
        nodes.removeAll(lockNodes);
        return nodes;
    }

    /**
     * 查找充电结束或者充电异常回退位置
     * @param nodeDB
     * @return
     */
    public static Node getBackNode(NodeDB nodeDB) {
        Node node = null;
        byte i = 0;
        if (nodeDB.getCanToUp()) {
            node = new Node(nodeDB.getGridx() - 1, nodeDB.getGridy(), nodeDB.getGridz());
            i++;
        }
        if (nodeDB.getCanToDown()) {
            node = new Node(nodeDB.getGridx() + 1, nodeDB.getGridy(), nodeDB.getGridz());
            i++;
        }
        if (nodeDB.getCanToLeft()) {
            node = new Node(nodeDB.getGridx(), nodeDB.getGridy() - 1, nodeDB.getGridz());
            i++;
        }
        if (nodeDB.getCanToRight()) {
            node = new Node(nodeDB.getGridx(), nodeDB.getGridy() + 1, nodeDB.getGridz());
            i++;
        }
        if (i == 1)
            return node;
        throw new RuntimeException("充电桩可行驶方向不正确，请手动添加后退方案");
    }


}
